package ar.com.hjg.pngj.pixels;

import java.util.zip.Deflater;

import ar.com.hjg.pngj.IDatChunkWriter;
import ar.com.hjg.pngj.PngjOutputException;

/**
 * This class uses a quick compressor to get a rough estimate of deflate compression ratio.
 * 
 * This just ignores the outputStream, and the deflater related parameters
 */
public class CompressorStreamLz4 extends CompressorStream {

  private final DeflaterEstimatorLz4 lz4;

  private byte[] buf; // lazily allocated, only if needed
  private final int buffer_size;
  // bufpos=bytes in buffer yet not compressed (bytesIn include this)
  private int inbuf = 0;

  private static final int MAX_BUFFER_SIZE = 16000;

  public CompressorStreamLz4(IDatChunkWriter os, int maxBlockLen, long totalLen) {
    super(os, maxBlockLen, totalLen);
    lz4 = new DeflaterEstimatorLz4();
    buffer_size = (int) (totalLen > MAX_BUFFER_SIZE ? MAX_BUFFER_SIZE : totalLen);
  }

  public CompressorStreamLz4(IDatChunkWriter os, int maxBlockLen, long totalLen, Deflater def) {
    this(os, maxBlockLen, totalLen);// edlfater ignored
  }

  public CompressorStreamLz4(IDatChunkWriter os, int maxBlockLen, long totalLen,
      int deflaterCompLevel, int deflaterStrategy) {
    this(os, maxBlockLen, totalLen); // paramters ignored
  }

  @Override
  public void mywrite(byte[] b, int off, int len) {
    if (len == 0)
      return;
    if (done || closed)
      throw new PngjOutputException("write beyond end of stream");
    bytesIn += len;
    while (len > 0) {
      if (inbuf == 0 && (len >= MAX_BUFFER_SIZE || bytesIn == totalbytes)) {
        // direct copy (buffer might be null or empty)
        bytesOut += lz4.compressEstim(b, off, len);
        len = 0;
      } else {
        if (buf == null)
          buf = new byte[buffer_size];
        int len1 = inbuf + len <= buffer_size ? len : buffer_size - inbuf; // to copy
        if (len1 > 0)
          System.arraycopy(b, off, buf, inbuf, len1);
        inbuf += len1;
        len -= len1;
        off += len1;
        if (inbuf == buffer_size)
          compressFromBuffer();
      }
    }
  }

  void compressFromBuffer() {
    if (inbuf > 0) {
      bytesOut += lz4.compressEstim(buf, 0, inbuf);
      inbuf = 0;
    }
  }

  @Override
  public void done() {
    if (!done) {
      compressFromBuffer();
      done = true;
    }
  }

  @Override
  public void close() {
    done();
    if (!closed) {
      super.close();
      buf = null;
    }
  }

  public void reset() {
    super.reset();
  }

}
